package com.googlecode.openrtp.spi;

import java.util.Arrays;

/**
 *
 *
 * <pre>
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |V=2|P|X|  CC   |M|     PT      |     sequence number (SN)      |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                         timestamp (TS)                        |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |           synchronization source (SSRC) identifier            |
 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 * |            contributing source (CSRC) identifiers             |
 * |                             ....                              |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * </pre>
 *
 * @author jocke
 */
public final class RtpPacket {

    public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

    public static final long[] EMPTY_LONG_ARRAY = new long[0];

    public static final int RTP_PACKET_MIN_LENGTH = 12;
    /* Version number (2 bits). */
    private int v = 0;
    
    /* Padding (1 bit) */
    private int p = 0;
    
    /* Header extension (1 bit). */
    private int x = 0;
    
    /* CSRC count (4 bits). */
    private int cc = 0;
    
    /* Marker (1 bit). */
    private int m = 0;
    
    /* Payload type (7 bits). */
    private int pt = 0;
    
    /* Sequence number (16 bits). */
    private int sn = 0;
    
    /* Time stamp (32 bits). */
    private long ts = 0;
    
    /* Synchronization source (32 bits). */
    
    private long ssrc = 0;
    /* contributing source (CSRC) identifiers */

    private long[] csrc = EMPTY_LONG_ARRAY;
    /* the data */

    private byte[] payload = EMPTY_BYTE_ARRAY;

    private final int hash;

    private RtpPacket(int v, int p, int x, int cc, int m, int pt, int sn, long ts, long ssrc, long[] csrc, byte[] payload) {
        this.v = v;
        this.p = p;
        this.x = x;
        this.cc = cc;
        this.m = m;
        this.ssrc = ssrc;
        this.csrc = csrc;
        this.payload = payload;
        this.pt = pt;
        this.sn = sn;
        this.ts = ts;
        this.hash = hash();
    }

    public static RtpPacket create(int v, int p, int x, int cc, int m, int pt, int sn, long ts, long ssrc, long[] csrc, byte[] payload) {
        return new RtpPacket(v, p, x, cc, m, pt, sn, ts, ssrc, csrc, payload);
    }

    public static RtpPacket create(int v, int p, int x, int cc, int m, int pt, int sn, long ts, long ssrc, byte[] payload) {
        return new RtpPacket(v, p, x, cc, m, pt, sn, ts, ssrc, EMPTY_LONG_ARRAY, payload);
    }

    public int getV() {
        return v;
    }

    public int getP() {
        return p;
    }

    public int getX() {
        return x;
    }

    public int getCc() {
        return cc;
    }

    public int getM() {
        return m;
    }

    public int getPt() {
        return pt;
    }

    public int getSn() {
        return sn;
    }

    public long getTs() {
        return ts;
    }

    public long getSsrc() {
        return ssrc;
    }

    public byte[] getPayload() {
        return payload;
    }

    public long[] getCsrc() {
        return csrc;
    }

    @Override
    public String toString() {
        return "RtpPacket{" + "v=" + v + ", p=" + p + ", x=" + x + ", cc=" + cc
                + ", m=" + m + ", pt=" + pt + ", sn=" + sn + ", ts=" + ts
                + ", ssrc=" + ssrc + ", csrc=" + Arrays.toString(csrc)
                + ", payload=" + payload.length + '}';
    }

    public int getSize() {
        return RTP_PACKET_MIN_LENGTH + payload.length;
    }

    @Override
    public int hashCode() {
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final RtpPacket other = (RtpPacket) obj;
        if (this.v != other.v) {
            return false;
        }

        return this.hash == other.hash;
    }

    private int hash() {
        int h = 3;
        h = 79 * h + this.v;
        h = 79 * h + this.p;
        h = 79 * h + this.x;
        h = 79 * h + this.cc;
        h = 79 * h + this.m;
        h = 79 * h + this.pt;
        h = 79 * h + this.sn;
        h = 79 * h + (int) (this.ts ^ (this.ts >>> 32));
        h = 79 * h + (int) (this.ssrc ^ (this.ssrc >>> 32));
        h = 79 * h + Arrays.hashCode(this.csrc);
        h = 79 * h + Arrays.hashCode(this.payload);
        return h;
    }
}
